/*
 * SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: CC0-1.0
 */

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "misc/lv_color.h"
#include "sdkconfig.h"

#include "lvgl.h"
#include <assert.h>
#include <inttypes.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "esp_err.h"
#include "esp_log.h"
#include "esp_timer.h"
#include <sys/time.h>
#include <time.h>

#include "src/widgets/label/lv_label.h"

#include "driver/gpio.h"
#include "esp_mac.h"
#include "esp_netif.h"
#include "esp_now.h"
#include "esp_wifi.h"
#include "freertos/semphr.h"
#include "nvs_flash.h"

#include "esp_random.h"
#include "widgets/dropdown/lv_dropdown.h"

static char espnow_str[250];

#define ESP_PARAM_CHECK(con)                                                   \
  do {                                                                         \
    if (!(con)) {                                                              \
      ESP_LOGE(TAG, "[%s, %d]: <ESP_ERR_INVALID_ARG> !(%s)", __func__,         \
               __LINE__, #con);                                                \
    }                                                                          \
  } while (0)

#define TAG "LVGL"

static uint8_t local_node_mac[ESP_NOW_ETH_ALEN] = {0};

typedef enum {
  ESPNOW_LED_STATUS_UPLOAD,
  ESPNOW_LED_SET_STATUS,
} espnow_event_id_t;

typedef struct {
  uint8_t mac_addr[ESP_NOW_ETH_ALEN];
  esp_now_send_status_t status;
  espnow_event_id_t event;
  uint32_t count;
} espnow_data_t;

lv_obj_t *btn = NULL;
lv_obj_t *textlabel = NULL;
// static int32_t g_rssi = 0;

static void btn_event_cb(lv_event_t *e) {
  lv_event_code_t code = lv_event_get_code(e);
  lv_obj_t *btn = lv_event_get_target(e);

  if (code == LV_EVENT_CLICKED) {
    uint32_t current_time = esp_timer_get_time() / 1000;
    static bool status = true;
    static uint32_t last_check_time = 0;
    if (current_time - last_check_time >= 300) {
      // static uint8_t cnt = 0;
      // cnt++;
      last_check_time = current_time;
      /*Get the first child of the button which is the label and change its
       * text*/
      lv_obj_t *label = lv_obj_get_child(btn, 0);

      if (status) {
        lv_obj_set_style_bg_color(btn, lv_palette_main(LV_PALETTE_CYAN), 0);
        lv_label_set_text(label, "LED Off");
      } else {
        lv_obj_set_style_bg_color(btn, lv_palette_main(LV_PALETTE_AMBER), 0);
        lv_label_set_text(label, "LED On");
      }
      status = !status;
    }
  }
}

// static void event_handler(lv_event_t *e) {
//   lv_event_code_t code = lv_event_get_code(e);
//   lv_obj_t *obj = lv_event_get_target(e);
//   if (code == LV_EVENT_VALUE_CHANGED) {
//     LV_UNUSED(obj);
//     const char *txt = lv_checkbox_get_text(obj);
//     const char *state =
//         lv_obj_get_state(obj) & LV_STATE_CHECKED ? "Checked" : "Unchecked";
//     LV_UNUSED(txt);
//     LV_UNUSED(state);
//     ESP_LOGI(TAG, "%s: %s", txt, state);
//   }
// }

/**
 * Create a button with a label and react on click event.
 */
void lv_example_get_started_2(void) {
  LV_IMG_DECLARE(ff3);

  lv_obj_t *img_obj;
  static lv_style_t bg_style; // 创建一个样式结构体
  lv_style_init(&bg_style);   // 初始化样式结构体

  img_obj = lv_img_create(lv_screen_active()); // 在容器中创建Image对象
  lv_img_set_src(img_obj, &ff3);               // 设置图片源
                                               // 设置背景图片源
  lv_style_set_bg_img_src(&bg_style,
                          img_obj); // your_image_resource应指向图片资源
  lv_obj_t *scr = lv_screen_active(); // 获取当前活动屏幕
  lv_obj_add_style(scr, &bg_style, LV_STATE_DEFAULT);

  static lv_style_t f_style; // 创建一个样式结构体
  LV_FONT_DECLARE(FiraCode_Light);
  lv_style_set_text_font(&f_style, &FiraCode_Light);

  btn =
      lv_button_create(lv_screen_active()); /*Add a button the current screen*/

  lv_obj_set_style_bg_color(btn, lv_palette_main(LV_PALETTE_AMBER), 0);
  lv_obj_set_style_radius(btn, 5, LV_PART_MAIN | LV_STATE_DEFAULT);

  lv_obj_set_style_bg_opa(btn, 160, LV_PART_MAIN | LV_STATE_DEFAULT);

  lv_obj_set_pos(btn, 40, 160);  /*Set its position*/
  lv_obj_set_size(btn, 100, 50); /*Set its size*/
  lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL,
                      NULL); /*Assign a callback to the button*/

  lv_obj_t *label = lv_label_create(btn); /*Add a label to the button*/
  lv_label_set_text(label, "Button");     /*Set the labels text*/
  lv_obj_center(label);

  /*Create a white label, set its text and align it to the center*/
  textlabel = lv_label_create(lv_screen_active());
  lv_label_set_text(textlabel, "Hello world! This demo\\");

  lv_obj_add_style(textlabel, &f_style, LV_STATE_DEFAULT);

  lv_obj_set_style_text_color(lv_screen_active(), lv_color_hex(0x333333),
                              LV_PART_MAIN);
  lv_obj_align(textlabel, LV_ALIGN_CENTER, 0, -32);
}

static void example_increase_lvgl_tick(void *arg) {
  /* Tell LVGL how many milliseconds has elapsed */
  lv_tick_inc(50);
}

extern void lv_port_disp_init(void);
extern void lv_port_indev_init(void);

/* ESPNOW can work in both station and softap mode. It is configured in
 * menuconfig. */
#if CONFIG_ESPNOW_WIFI_MODE_STATION
#define ESPNOW_WIFI_MODE WIFI_MODE_STA
#define ESPNOW_WIFI_IF ESP_IF_WIFI_STA
#else
#define ESPNOW_WIFI_MODE WIFI_MODE_AP
#define ESPNOW_WIFI_IF ESP_IF_WIFI_AP
#endif

#define BLINK_GPIO 15

#define ESPNOW_QUEUE_SIZE 6

#define IS_BROADCAST_ADDR(addr)                                                \
  (memcmp(addr, s_example_broadcast_mac, ESP_NOW_ETH_ALEN) == 0)

#define ESPNOW_MAXDELAY 512
#define EXAMPLE_ESPNOW_DATA_MAX 10
#define CONFIG_ESPNOW_CHANNEL 1

/* WiFi should start before using ESPNOW */
static void example_wifi_init(void) {
  ESP_ERROR_CHECK(esp_netif_init());
  ESP_ERROR_CHECK(esp_event_loop_create_default());
  wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
  ESP_ERROR_CHECK(esp_wifi_init(&cfg));
  ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
  ESP_ERROR_CHECK(esp_wifi_set_mode(ESPNOW_WIFI_MODE));
  ESP_ERROR_CHECK(esp_wifi_start());
  ESP_ERROR_CHECK(
      esp_wifi_set_channel(CONFIG_ESPNOW_CHANNEL, WIFI_SECOND_CHAN_NONE));

#if CONFIG_ESPNOW_ENABLE_LONG_RANGE
  ESP_ERROR_CHECK(esp_wifi_set_protocol(
      ESPNOW_WIFI_IF, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G |
                          WIFI_PROTOCOL_11N | WIFI_PROTOCOL_LR));
#endif
}

static QueueHandle_t g_espnow_queue = NULL;
static uint32_t g_magic_cache = 0;
typedef struct {
  uint32_t magic;
  size_t size;
  uint8_t desc_mac[6];
  uint8_t data[100];
} espnow_recv_data_t;

static int8_t rssi = 0;

static void example_espnow_recv_cb(const esp_now_recv_info_t *recv_info,
                                   const uint8_t *data, int len) {
  ESP_PARAM_CHECK(recv_info);
  ESP_PARAM_CHECK(data);
  ESP_PARAM_CHECK(len <= 255);

  if (g_espnow_queue) {
    espnow_recv_data_t *rec_data = (espnow_recv_data_t *)data;
    rssi = recv_info->rx_ctrl->rssi;
    if (xQueueSend(g_espnow_queue, rec_data, 0) != pdPASS) {
      ESP_LOGW(TAG, "[%s, %d] Send iperf recv queue failed", __func__,
               __LINE__);
    }
  }
}

uint8_t sec_mac[ESP_NOW_ETH_ALEN] = {0x54, 0x32, 0x04, 0x0b, 0x4a, 0xb0};
uint8_t mac[ESP_NOW_ETH_ALEN] = {0x24, 0xec, 0x4a, 0x27, 0x16, 0x2c};

static esp_err_t example_espnow_init(void) {
  /* Initialize ESPNOW and register sending and receiving callback function. */
  ESP_ERROR_CHECK(esp_now_init());
  ESP_ERROR_CHECK(esp_now_register_recv_cb(example_espnow_recv_cb));

  /* Add broadcast peer information to peer list. */
  esp_now_peer_info_t *peer = malloc(sizeof(esp_now_peer_info_t));
  if (peer == NULL) {
    ESP_LOGE(TAG, "Malloc peer information fail");
    esp_now_deinit();
    return ESP_FAIL;
  }
  memset(peer, 0, sizeof(esp_now_peer_info_t));
  peer->channel = CONFIG_ESPNOW_CHANNEL;
  peer->ifidx = ESPNOW_WIFI_IF;
  peer->encrypt = false;
  memcpy(peer->peer_addr, mac, ESP_NOW_ETH_ALEN);
  ESP_ERROR_CHECK(esp_now_add_peer(peer));

  return ESP_OK;
}

static uint32_t send_time_count[6] = {0};

void espnow_task() {
  static uint32_t good_count = 1;
  static uint32_t gc = 0;
  static uint32_t bad_count = 0;
  static uint64_t send_time = 1;
  static uint32_t send_count = 0;
  g_espnow_queue = xQueueCreate(10, sizeof(espnow_recv_data_t));

  /* handle the tasks of LVGL */
  uint32_t start = esp_timer_get_time();
  uint32_t time = 1;
  while (1) {
    espnow_recv_data_t device_pkg = {0};
    // 向设备发起获取设备待发送数据包的请求
    //  从队列获取最新的设备数据包
    for (int i = 0; i < 100; i++) {
      device_pkg.magic = esp_random();
      memcpy(device_pkg.desc_mac, mac, 6);
      g_magic_cache = device_pkg.magic;
      uint32_t end = esp_timer_get_time() + 100 * 1000;
      bool pkg_good = false;
      struct timeval tv_now;
      gettimeofday(&tv_now, NULL);
      uint64_t s_start = tv_now.tv_sec * 1000000L + tv_now.tv_usec;
      esp_now_send(mac, (uint8_t *)&device_pkg, sizeof(espnow_recv_data_t));
      do {
        if (g_espnow_queue && xQueueReceive(g_espnow_queue, &device_pkg,
                                            pdMS_TO_TICKS(50)) == pdPASS) {
          if (device_pkg.magic == g_magic_cache) {
            gettimeofday(&tv_now, NULL);
            uint64_t s_end = tv_now.tv_sec * 1000000L + tv_now.tv_usec;
            // ESP_LOGI(TAG, "espnow package %ld good, time %ld", good_count,
            //          (s_end - start) / 1000);
            uint32_t once_time = (s_end - s_start);
            if (once_time <= 3500) {
              send_time_count[1]++;
            } else if (3500 < once_time && once_time <= 3900) {
              send_time_count[2]++;
            } else if (3900 < once_time && once_time <= 4200) {
              send_time_count[3]++;
            } else if (4200 < once_time && once_time <= 4500) {
              send_time_count[4]++;
            } else if (4500 < once_time) {
              send_time_count[5]++;
            }
            send_time += once_time;
            send_count++;
            good_count++;
            pkg_good = true;
          }
        } else {
          bad_count++;
        }
      } while (!pkg_good && esp_timer_get_time() < end);
    }
    uint32_t send_end = esp_timer_get_time();
    time = send_end - start;
    if (time >= 1000 * 1000) {
      // ESP_LOGI(TAG, "espnow %ldkbps, loss %%%.03f, avg time %dms",
      //          (uint32_t)(good_count - gc) * sizeof(espnow_recv_data_t) * 8 /
      //              (time / 1000),
      //  (float_t)bad_count / (bad_count + good_count) * 100,
      //  (uint8_t)(send_time / (good_count - gc)));
      float bc = 0.00;
      if (bad_count > 0) {
        bc = (float_t)bad_count / (bad_count + good_count) * 100;
      }
      uint32_t best_send = 0;
      uint32_t b_index = 0;
      for (int i = 0; i < 6; i++) {
        if (send_time_count[i] > best_send) {
          best_send = send_time_count[i];
          b_index = i;
        }
      }
      sprintf(espnow_str,
              "%ldkbps\n   %%%.03f \navg %luus\nrssi: %d\n best %ld, %%%.03f",
              (uint32_t)(good_count - gc) * sizeof(espnow_recv_data_t) * 8 /
                  (time / 1000),
              bc, (uint32_t)(send_time / (good_count - gc)), rssi, b_index,
              (float_t)best_send / (send_count)*100);

      if (good_count < bad_count) {
        bad_count = 0;
      }
      send_count = 1;
      gc = good_count - 1;
      time = 1;
      send_time = 1;
      start = esp_timer_get_time();
      for (int i = 0; i < 6; i++) {
        send_time_count[i] = 0;
      }
    }
  }
}

void app_main(void) {
  // Initialize NVS
  esp_err_t ret = nvs_flash_init();
  if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
      ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
    ESP_ERROR_CHECK(nvs_flash_erase());
    ret = nvs_flash_init();
  }
  ESP_ERROR_CHECK(ret);

  example_wifi_init();
  example_espnow_init();
  //  读取本地MAC地址
  esp_wifi_get_mac(ESP_IF_WIFI_STA, local_node_mac);

  // // 获取系统滴答频率
  // const TickType_t tick_rate_hz = configTICK_RATE_HZ;

  // // 计算时间片长度（以毫秒为单位）
  // const TickType_t time_slice_length_ms = 1000 / tick_rate_hz;

  // ESP_LOGI("Time Slice", "Time slice length: %ld ms", time_slice_length_ms);

#define COUNT_TIME 100

#if CONFIG_IDF_TARGET_ESP32C6
  gpio_reset_pin(BLINK_GPIO);
  /* Set the GPIO as a push/pull output */
  gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
#endif

  lv_init();
  lv_port_disp_init();
  lv_port_indev_init();

  ESP_LOGI(TAG, "Install LVGL tick timer");
  // Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
  const esp_timer_create_args_t lvgl_tick_timer_args = {
      .callback = &example_increase_lvgl_tick, .name = "lvgl_tick"};
  esp_timer_handle_t lvgl_tick_timer = NULL;
  ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
  ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, 2 * 1000));
  lv_example_get_started_2();
  xTaskCreate(espnow_task, "espnow_main", 8192, NULL, tskIDLE_PRIORITY + 1,
              NULL);
  while (true) {
    lv_task_handler();
    vTaskDelay(pdMS_TO_TICKS(50));
    // lv_label_set_text(textlabel, espnow_str);
  }
}